home *** CD-ROM | disk | FTP | other *** search
Wrap
/******************************************************************** * $Author: drich $ * $Revision: 1.1 $ * $Date: 1995/10/03 04:08:04 $ * $Source: /proj/freeware1.0/gopher1.12/src/gopher/RCS/gopher.c,v $ * $State: Exp $ * * Paul Lindner, University of Minnesota CIS. * * Copyright 1991, 1992 by the Regents of the University of Minnesota * see the file "Copyright" in the distribution for conditions of use. ********************************************************************* * MODULE: gopher.c * Main functions for the gopher client ********************************************************************* * Revision History: * $Log: gopher.c,v $ * Revision 1.1 1995/10/03 04:08:04 drich * gopher 1.2 check-in * * Revision 1.15 1993/01/17 03:46:46 lindner * Fixes for tmpname memory leak. * * Revision 1.14 1993/01/15 20:14:27 lindner * Added -T to the usage line * * Revision 1.13 1993/01/14 22:00:21 lindner * added ^R and ^W to redraw the screen for VMS * Old search terms don't persist now when highlighting text. * * Revision 1.12 1993/01/13 16:18:11 lindner * Put back in 's' save patch... Sigh... * * Revision 1.11 1993/01/12 22:38:56 lindner * Rescinded changes for sound on VMS, it doesn't work! * * Revision 1.10 1993/01/12 21:42:14 lindner * Fixed problem with viewing files in secure mode. * Fixed problems with suck_sound in VMS. * Fixed problems with \n on the system command in VMS * * Revision 1.9 1993/01/12 17:30:17 lindner * Fixed problem with 's' key for save_file. * * Revision 1.8 1993/01/09 02:33:46 lindner * Fixed definitions for controlc() and sizechange() * * Revision 1.7 1993/01/09 02:18:40 lindner * Changed (void*)-1 constructs to SIG_ERR * * Revision 1.6 1993/01/08 19:25:02 lindner * Securemode users can't display graphics now, they get a message instead.. * * Revision 1.5 1993/01/07 22:49:40 lindner * Added option -T to set initial type. Added 'D' command for downloading * * Revision 1.4 1992/12/31 06:34:49 lindner * Okay, okay, Save_File is really Save_file * * Revision 1.3 1992/12/31 05:38:01 lindner * Removed getfile() replaced with Save_File(). * * Revision 1.2 1992/12/31 04:34:38 lindner * Added VMS support from fogel and jqj. * * Revision 1.1 1992/12/10 23:32:16 lindner * gopher 1.1 release * *********************************************************************/ #include "gopher.h" void describe_gopher(); /* ** Open a connection to another host using telnet or tn3270 */ void do_tel_3270(ZeGopher) GopherStruct *ZeGopher; { char *Dialogmess[20]; char sMessage1[128]; char sMessage2[128]; char sTelCmd[128]; /* retrieve the gopher information for the telnet command*/ clear(); Dialogmess[0] = "Warning!!!!!, you are about to leave the Internet"; Dialogmess[1] = "Gopher program and connect to another host. If"; Dialogmess[2] = "you get stuck press the control key and the ] key,"; Dialogmess[3] = "and then type quit"; Dialogmess[4] = ""; if (GSgetPort(ZeGopher) != 0) sprintf(sMessage1,"Connecting to %s, port %d using %s.", GSgetHost(ZeGopher),GSgetPort(ZeGopher), (GSgetType(ZeGopher) == A_TN3270) ? "tn3270" : "telnet"); else sprintf(sMessage1, "Connecting to %s using %s.", GSgetHost(ZeGopher),(GSgetType(ZeGopher) == A_TN3270) ? "tn3270" : "telnet"); Dialogmess[5] = sMessage1; if (*GSgetPath(ZeGopher) != '\0') sprintf(sMessage2,"Use the account name \"%s\" to log in", GSgetPath(ZeGopher)); else sMessage2[0] = '\0'; Dialogmess[6] = ""; Dialogmess[7] = sMessage2; Dialogmess[8] = NULL; if (CURDialog(CursesScreen, GSgetTitle(ZeGopher), Dialogmess) <0) return; CURexit(CursesScreen); if (GSgetType(ZeGopher) == 'T') { /**** A TN3270 connection ****/ sprintf(sTelCmd, "%s %s", STRget(TN3270Command), GSgetHost(ZeGopher)); } else if (GSgetPort(ZeGopher) != 0 && GSgetPort(ZeGopher) != 23) #if defined(VMS) && defined(MULTINET) sprintf(sTelCmd, "%s %s /PORT=%d", #else sprintf(sTelCmd, "%s %s %d", #endif STRget(TelnetCommand), GSgetHost(ZeGopher), GSgetPort(ZeGopher)); else sprintf(sTelCmd, "%s %s", STRget(TelnetCommand), GSgetHost(ZeGopher)); CURexit(CursesScreen); system(sTelCmd); CURenter(CursesScreen); return; } /* ** do_index gets keywords from the user to search for. It returns ** it to the calling process. This storage is volotile. Callers should ** make a copy if they want to call do_index multiple times. */ char* do_index(ZeGopher) GopherStruct *ZeGopher; { static char *inputline = NULL; static char *prompt[2]; static char *response[2]; if (inputline == NULL) { inputline = (char *) malloc(sizeof(char)*256); if (inputline == NULL) perror("Out of memory"), exit(-1); *inputline = '\0'; } prompt[0] = "Words to search for"; prompt[1] = NULL; response[0] = inputline; response[1] = NULL; if (CURRequest(CursesScreen, GSgetTitle(ZeGopher),prompt, response) == -1 ) return(NULL); if (*inputline == '\0') return(NULL); else return(inputline); } /* * this procedure just retrieves binary data from the socket and * pumps it into a "play" process. */ #define BUFSIZE 1400 /* A pretty good value for ethernet */ #ifndef VMS void suck_sound(sockfd) int sockfd; { FILE *Play; int j; char buf[BUFSIZE]; if (*STRget(PlayCommand) == '\0') { /*** Hey! no play command, bummer ***/ CursesErrorMsg("Sorry, this machine doesn't support sounds"); return; } Play = popen(STRget(PlayCommand), "w"); while(1) { j = read(sockfd, buf, BUFSIZE); if (j == 0) break; fwrite(buf, 1, j, Play); } } #endif /* * fork off a sound process to siphon the data across the net. * So the user can listen to tunage while browsing the directories. */ void do_sound(ZeGopher) GopherStruct *ZeGopher; { #ifdef VMS CursesErrorMsg("Sorry, this machine doesn't support sounds"); #else int sockfd; char sTmp[5]; BOOLEAN Waitforchld = FALSE; sTmp[0] = '\0'; if ((sockfd = GSconnect(ZeGopher)) <0) { check_sock(sockfd, GSgetHost(ZeGopher), GSgetPort(ZeGopher)); return; } /** Send out the request **/ writestring(sockfd, GSgetPath(ZeGopher)); writestring(sockfd, "\r\n"); /** Okay, it's cool, we can fork off **/ if (SOUNDCHILD != 0) Waitforchld = TRUE; if ( (SOUNDCHILD = fork()) < 0) ;/* Fork Error */ else if (SOUNDCHILD == 0) { /* Child Process */ wait(SIGCHLD); suck_sound(sockfd); exit(0); } /* Parent Process */ closenet(sockfd); return; #endif /* not VMS */ } /* * Replace the searched words with backspaces and underline characters. */ static char sGBoldoutput[20]; /*** Used for stripping weird stuff from term strings ***/ static int iGposition = 0; /*** Pointer into the Boldoutput string **/ /*** Used by tputs() ***/ int Boldoutchar(c) char c; { sGBoldoutput[iGposition++] = c; return(c); } void Boldit(inputline, outputline, MungeSearchstr) char *inputline, *outputline, *MungeSearchstr; { char words[20][40]; /** A reasonable guess **/ int numchars, lowwordnum, wordcount, i; char *cp, *lowword; outputline[0] = '\0'; bzero(outputline, 512); while (isspace(*MungeSearchstr)) /** Strip off spaces **/ MungeSearchstr++; for (wordcount=0; wordcount<20; wordcount++) { while (isspace(*MungeSearchstr)) /** Strip off spaces **/ MungeSearchstr++; numchars = sreadword(MungeSearchstr, words[wordcount], 40); MungeSearchstr += numchars; if (numchars == 0) break; if (strcmp(words[wordcount], "and")==0 || strcmp(words[wordcount], "or")==0 || strcmp(words[wordcount], "not")==0) { words[wordcount][0] = '\0'; wordcount--; } } /** Find the first word in the line **/ while (*inputline!='\0') { lowword = NULL; for (i=0; i< wordcount; i++) { cp = strcasestr(inputline, words[i]); if (cp != NULL) if (cp < lowword || lowword == NULL) { lowword = cp; lowwordnum = i; } } if (lowword == NULL) { strcpy(outputline, inputline); return; } else { strncpy(outputline, inputline, lowword - inputline); outputline += (lowword - inputline); inputline = lowword; iGposition = 0; tputs(CURgetHighon(CursesScreen), 1, Boldoutchar); sGBoldoutput[iGposition] = '\0'; strcpy(outputline, sGBoldoutput); outputline += strlen(sGBoldoutput); strncpy(outputline, inputline, strlen(words[lowwordnum])); inputline += strlen(words[lowwordnum]); outputline += strlen(words[lowwordnum]); iGposition = 0; tputs(CURgetHighoff(CursesScreen), 1, Boldoutchar); sGBoldoutput[iGposition] = '\0'; strcpy(outputline, sGBoldoutput); outputline += strlen(sGBoldoutput); } } } /** *** Show file takes a gopher text thing, writes it to a file *** and passes it to your favorite pager. **/ void showfile(ZeGopher) GopherStruct *ZeGopher; { int i=0, iLength, sockfd; char *tmpfilename; FILE *tmpfile; char inputline[512]; char outputline[512]; char sTmp[5]; int twirlcounter=0; sTmp[0] = '\0'; /** Open a temporary file **/ Gopenfile = tmpfilename = tempnam("/tmp","gopher"); if ((tmpfile = fopen(tmpfilename, "w")) == NULL) fprintf(stderr, "Couldn't make a tmp file!\n"), exit(-1); /** Send out the request **/ if ((sockfd = GSconnect(ZeGopher)) <0) { check_sock(sockfd, GSgetHost(ZeGopher), GSgetPort(ZeGopher)); return; } writestring(sockfd, GSgetPath(ZeGopher)); writestring(sockfd, "\r\n"); /*** Case here on the object type, Get it line-by-line for text, binary mode if it's supported. ***/ if (GSgetType(ZeGopher) == A_IMAGE || GSgetType(ZeGopher) == A_GIF) { char buf[BUFSIZE]; int cc = 0; int counter = 0; while ( (cc = readn( sockfd, buf, BUFSIZE )) > 0 ) { counter += cc; if (write(fileno(tmpfile), buf, cc ) <=0) CursesErrorMsg("Problems Writing File"); twirl(); } if ( cc < 0 ) CursesErrorMsg( "Warning! File may be corrupt!"); else { CursesErrorMsg( "File received successfully" ); } fclose( tmpfile ); } else { while (1) { /** make a mark for every page **/ twirlcounter++; if ((twirlcounter % 25) == 0) twirl(); iLength = readline(sockfd, inputline, 512); outputline[0] = '\0'; if (iLength == 0) break; ZapCRLF(inputline); /*** Ugly hack ahead..... ***/ if (GSgetType(ZeGopher) == A_CSO) { if (inputline[0] == '2') break; if ((inputline[0] >= '3') && (inputline[0] <= '9')) { fprintf(tmpfile, "%s\n", GSgetPath(ZeGopher)); fprintf(tmpfile, "%s\n", inputline+4); break; } if (inputline[0] == '-') { if (inputline[1] >= '3' && inputline[1] <= '9') { fprintf(tmpfile, "%s\n", GSgetPath(ZeGopher)); fprintf(tmpfile, "%s\n", inputline+5); } else { char *colonpos = strchr(inputline+5,':'); if (colonpos != NULL && *(colonpos-1) != i) { fprintf(tmpfile, "-------------------------------------------------------\n"); i = *(colonpos-1); } fputs((colonpos ? colonpos+1 : inputline+6), tmpfile); fputc('\n', tmpfile); } } } if (GSgetType(ZeGopher) == A_FILE) { if ((inputline[0] == '.') && (inputline[1] == '\0')) break; else { /*** Underline searched words, except and, or and not ***/ if (Searchstring != NULL) { Boldit(inputline, outputline, Searchstring); } else strcpy(outputline, inputline); fputs(outputline, tmpfile); fputc('\n', tmpfile); } } else if (GSgetType(ZeGopher) == A_MIME) { if ((inputline[0] == '.') && (inputline[1] == '\0')) break; else { fputs(inputline, tmpfile); fputc('\n', tmpfile); } } } /** Work around a bug in xterm n' curses*/ fprintf(tmpfile, "\012 \n\n"); (void)fclose(tmpfile); } closenet(sockfd); if (GSgetType(ZeGopher) == A_MIME) display_mime(tmpfilename, GSgetTitle(ZeGopher)); else if (GSgetType(ZeGopher) == A_IMAGE || GSgetType(ZeGopher)== A_GIF) display_image(tmpfilename, GSgetTitle(ZeGopher)); else display_file(tmpfilename, ZeGopher); /** Good little clients clean up after themselves..**/ if (unlink(tmpfilename)!=0) fprintf(stderr, "Couldn't unlink!!!\n"), exit(-1); Gopenfile = NULL; free(tmpfilename); CURenter(CursesScreen); return; } /* ** Pushgopher takes a GopherThing pointer and adds it to it's stack. ** ** Ick this must be fixed! */ void pushgopher(ZeDir) GopherDirObj *ZeDir; { OldDirs[iLevel]= ZeDir; iLevel ++; } /* ** If the stack is empty, popgopher returns a -1 */ int popgopher(ZeDir) GopherDirObj **ZeDir; { if (iLevel == 0) return(-1); iLevel --; *ZeDir = OldDirs[iLevel]; return(0); } void check_sock(sockfd, host, port) int sockfd; char *host; int port; { char DispString[WHOLELINE]; char *DispStrings[4]; if (sockfd <0) { sprintf(DispString, "Cannot connect to host %s, port %d.", host, port); switch (sockfd) { case -2: DispStrings[2] = "Hostname is unknown."; break; case -3: DispStrings[2] = "Unable to allocate a socket."; break; case -4: DispStrings[2] = "Connection refused by host."; break; default: DispStrings[2] = "Unknown error."; } DispStrings[0] = DispString; DispStrings[1] = ""; DispStrings[3] = NULL; CURDialog(CursesScreen, "Network Error", DispStrings); } } BOOLEAN ReallyQuit() { char yesno[3]; yesno[0] = 'y'; yesno[1] = '\0'; CURgetYesorNo(CursesScreen, "Really quit (y/n) ?", yesno); if (*yesno == 'y') { return(TRUE); } return(FALSE); } /*************** ** Abort() catches sigpipes and other unknown signals */ void Abort(sig) int sig; { if (Gopenfile != NULL) unlink(Gopenfile); exit(-1); } /************** ** This bit of code catches control-c's, it cleans up the curses stuff. */ void controlc(sig) int sig; { if (Gopenfile != NULL) if (unlink(Gopenfile) < 0) if (errno != ENOENT) fprintf(stderr, "could not unlink %s\n", Gopenfile); #ifdef VMS if (1) #else if (ReallyQuit()) #endif { CURexit(CursesScreen); if (ChangedDefs) write_rc(); exit(0); } else { CURresize(CursesScreen); scline(-1, 1, CurrentDir); /** Interrupt search, go back a level?? **/ } /* * Reprime the signals... */ if (signal(SIGINT, controlc) == SIG_ERR) perror("signal died:\n"), exit(-1); } /************** ** This bit of code catches window size change signals **/ void sizechange(sig) int sig; { int lines, cols; #ifdef TIOCGWINSZ static struct winsize zewinsize; /* 4.3 BSD window sizing */ #endif lines = LINES; cols = COLS; #ifdef TIOCGWINSZ if (ioctl(0, TIOCGWINSZ, (char *) &zewinsize) == 0) { lines = zewinsize.ws_row; cols = zewinsize.ws_col; } else { #endif /* code here to use sizes from termcap/terminfo, not yet... */ ; #ifdef TIOCGWINSZ } if (lines != LINES || cols != COLS) { LINES = lines; COLS = cols; CURresize(CursesScreen); scline(-1, 1, CurrentDir); } if (signal(SIGWINCH, sizechange)==SIG_ERR) perror("signal died:\n"), exit(-1); #endif } /********** ** ** Set up all the global variables. ** ***********/ void Initialize() { PagerCommand = STRnew(); PrinterCommand = STRnew(); TelnetCommand = STRnew(); TN3270Command = STRnew(); PlayCommand = STRnew(); MailCommand = STRnew(); EditorCommand = STRnew(); MIMECommand = STRnew(); ImageCommand = STRnew(); /** get defaults from the rc file **/ set_defs(); read_rc(); read_env(); /*** Set up the curses environment ***/ CursesScreen = CURnew(); if (strcmp(CURgetTerm(CursesScreen), "unknown")==0) fprintf(stderr, "I don't understand your terminal type\n"), exit(-1); /*** Make a signal handler for window size changes ***/ #ifdef SIGWINCH CURsetSIGWINCH(CursesScreen, sizechange); if (signal(SIGWINCH, sizechange)==SIG_ERR) perror("signal died:\n"), exit(-1); #endif if (signal(SIGINT, controlc) == SIG_ERR) perror("signal died:\n"), exit(-1); if (signal(SIGPIPE, Abort) == SIG_ERR) perror("signal died:\n"), exit(-1); /*** Init MainWindow ****/ CURenter(CursesScreen); } static char *GlobalOptions[] = {"Pager Command", "Print Command", "Telnet Command", "Sound Command", "Mail Command", "3270 Emulator Command", "MIME pager", "Image Viewer", NULL}; void SetOptions() { static char *Responses[MAXRESP]; static int inited = FALSE; if (SecureMode) { CursesErrorMsg("Sorry, you are not allowed to set options in secure mode."); return; } if (inited == FALSE) { int i; for (i=0; i< 8; i++) { Responses[i] = (char *) malloc(sizeof(char) * MAXSTR); } Responses[i] = NULL; inited = TRUE; } strcpy(Responses[0], STRget(PagerCommand)); strcpy(Responses[1], STRget(PrinterCommand)); strcpy(Responses[2], STRget(TelnetCommand)); strcpy(Responses[3], STRget(PlayCommand)); strcpy(Responses[4], STRget(MailCommand)); strcpy(Responses[5], STRget(TN3270Command)); strcpy(Responses[6], STRget(MIMECommand)); strcpy(Responses[7], STRget(ImageCommand)); if (CURRequest(CursesScreen, "Options", GlobalOptions, Responses) == 0) { STRset(PagerCommand, Responses[0]); STRset(PrinterCommand, Responses[1]); STRset(TelnetCommand, Responses[2]); STRset(PlayCommand, Responses[3]); STRset(MailCommand, Responses[4]); STRset(TN3270Command, Responses[5]); STRset(MIMECommand, Responses[6]); STRset(ImageCommand, Responses[7]); ChangedDefs = TRUE; } } /* This should be a generalized stack type. This is icky for now... */ int main(argc, argv) int argc; char *argv[]; { BOOLEAN bDone = FALSE; char sTmp[80]; GopherStruct *RootGophers[2]; int numhosts = 2; int TypedChar; /*** for getopt processing ***/ int c; extern char *optarg; extern int optind; int errflag =0, i; int Garbled = TRUE; boolean Bkmarksfirst = FALSE; for (i=0; i<2; i++) { RootGophers[i] = GSnew(); GSsetType (RootGophers[i], A_DIRECTORY); GSsetPath (RootGophers[i],""); } /** Should generalize this to >2 hosts .... Sigh... ***/ GSsetHost (RootGophers[0], CLIENT1_HOST); GSsetPort (RootGophers[0], CLIENT1_PORT); GSsetHost (RootGophers[1], CLIENT2_HOST); GSsetPort (RootGophers[1], CLIENT2_PORT); if (CLIENT2_PORT == 0) numhosts = 1; sTmp[0] = '\0'; while ((c = getopt(argc, argv, "Dsbp:t:T:")) != -1) switch (c) { case 's': SecureMode = TRUE; break; case 'p': GSsetPath(RootGophers[0], optarg); GSsetPath(RootGophers[1], optarg); break; case 'T': GSsetType(RootGophers[0], *optarg); GSsetType(RootGophers[1], *optarg); break; case 't': GSsetTitle(RootGophers[0], optarg); GSsetTitle(RootGophers[1], optarg); break; case 'D': DEBUG = TRUE; break; case 'b': Bkmarksfirst = TRUE; break; case '?': errflag++; } if (errflag) { fprintf(stderr, "Usage: %s [-sb] [-T type] [-p path] [-t title] [hostname port]+\n", argv[0]); exit(-1); } /**** Get host #1 from the command line ****/ if (optind < argc) { GSsetHost(RootGophers[0], argv[optind]); GSsetHost(RootGophers[1], ""); /** Nuke the 2nd alternative host **/ GSsetPort(RootGophers[1], 0); numhosts = 1; optind++; } if (optind < argc) { GSsetPort(RootGophers[0], atoi(argv[optind])); optind++; } /*** Get host #2 from the command line... ***/ if (optind < argc) { GSsetHost(RootGophers[1], argv[optind]); numhosts = 2; optind++; } if (optind < argc) { GSsetPort(RootGophers[1], atoi(argv[optind])); optind++; } /*** If the title hasn't been set, then add a default title **/ if (GSgetTitle(RootGophers[0]) == NULL) { sprintf(sTmp, "Root gopher server: %s", GSgetHost(RootGophers[0])); GSsetTitle(RootGophers[0], sTmp); sprintf(sTmp, "Root gopher server: %s", GSgetHost(RootGophers[1])); GSsetTitle(RootGophers[1], sTmp); } /*** Set up global variables, etc. ***/ Initialize(); if (Bkmarksfirst) { CurrentDir = BookmarkDir; if (CurrentDir != NULL) GDaddGS(CurrentDir, RootGophers[0]); } else { int rnum; srand(time(NULL)); rnum = rand() % numhosts; process_request(RootGophers[rnum]); /* just process the command line entry */ if( GSgetType(RootGophers[0]) != A_DIRECTORY ) { refresh(); CURexit(CursesScreen); exit(0); } } if (CurrentDir == NULL || GDgetNumitems(CurrentDir) <= 0) { /* * We didn't get anything from that gopher server. Either * it is down, doesn't exist, or is empty or otherwise * busted. * * Try any alternative hosts that are available.. */ while (optind < argc && CurrentDir == NULL) { GSsetHost(RootGophers[0], argv[optind]); optind++; GSsetPort(RootGophers[0], atoi(argv[optind])); optind++; process_request(RootGophers[0]); } } if (CurrentDir == NULL || GDgetNumitems(CurrentDir) <= 0) { CURexit(CursesScreen); fprintf(stderr, "%s: Nothing received for main menu, can't continue\n", argv[0]); exit(1); } while (bDone == FALSE) { GetMenu(CurrentDir, &TypedChar, Garbled); Garbled = TRUE; switch(TypedChar) { case '\r': case '\n': /*** Select the designated item ***/ if (process_request(GDgetEntry(CurrentDir,GDgetCurrentItem(CurrentDir)-1))==1) ; /** Don't do anything if we failed, I guess **/ break; case '\014': /* ^L */ #ifdef VMS case '\022': /* ^R */ case '\027': /* ^W */ #endif /*** Redraw the screen ***/ break; case '\0': /*** What the heck? ***/ CursesErrorMsg("Strange Error occurred!"); break; case 'u': case 'U': { GopherDirObj *tempGdir; /*** Don't highlight texts which aren't search hits ***/ Searchstring = NULL; /*** Go up a directory level ***/ tempGdir = CurrentDir; /** Don't destroy root level directory, or bookmarks **/ if (popgopher(&CurrentDir)==0 && tempGdir != CurrentDir) { if (tempGdir != BookmarkDir) GDdestroy(tempGdir); } } break; case 's': /*** Save a file directly ***/ if (SecureMode == TRUE) { CursesErrorMsg("Sorry, can't save files in securemode"); break; } Save_file(GDgetEntry(CurrentDir,GDgetCurrentItem(CurrentDir)-1), NULL,NULL); break; case 'D': Download_file(GDgetEntry(CurrentDir,GDgetCurrentItem(CurrentDir)-1)); break; case 'v': /** View bookmark list **/ { if (BookmarkDir == NULL) { CursesErrorMsg("No bookmarks are defined"); break; } /** Don't push an empty gopher directory... **/ if (CurrentDir != NULL) pushgopher(CurrentDir); CurrentDir = BookmarkDir; break; } case 'a': /** add current item as a bookmark **/ { GopherObj *tmpgs; char newtitle[256]; if (BookmarkDir == NULL) { BookmarkDir = GDnew(32); GDsetTitle(BookmarkDir, "Bookmarks"); } tmpgs = GSnew(); GScpy(tmpgs, GDgetEntry(CurrentDir, GDgetCurrentItem(CurrentDir)-1)); strcpy(newtitle, GSgetTitle(tmpgs)); if (CURGetOneOption(CursesScreen, "Name for this bookmark? ", newtitle) <0) break; if (*newtitle == '\0') break; GSsetTitle(tmpgs, newtitle); GDaddGS(BookmarkDir, tmpgs); GSdestroy(tmpgs); ChangedDefs = TRUE; break; } case 'A': /*** Add current directory/search as a bookmark **/ { GopherObj *tmpgs; char newtitle[256]; if (BookmarkDir == NULL) { BookmarkDir = GDnew(32); GDsetTitle(BookmarkDir, "Bookmarks"); } if (CurrentDir == BookmarkDir) { CursesErrorMsg("Sorry, can't make a bookmark of bookmarks"); break; } tmpgs = GSnew(); if (iLevel == 0) GScpy(tmpgs, RootGophers[0]); else GScpy(tmpgs, GDgetEntry(OldDirs[iLevel-1], GDgetCurrentItem(OldDirs[iLevel-1]))); strcpy(newtitle, GDgetTitle(CurrentDir)); if (CURGetOneOption(CursesScreen, "Name for this bookmark? ", newtitle)<0) break; if (*newtitle == '\0') break; GSsetTitle(tmpgs, newtitle); /*** Freeze the search, if there was one. ***/ if (GSgetType(tmpgs) == '7') { char ickypath[512]; strcpy(ickypath, GSgetPath(tmpgs)); strcat(ickypath, "\t"); strcat(ickypath, Searchstring); GSsetPath(tmpgs, ickypath); GSsetType(tmpgs, '1'); } GDaddGS(BookmarkDir, tmpgs); ChangedDefs = TRUE; break; } case 'd': /*** Delete a bookmark ***/ { GopherDirObj *tempgd; int i; if (GDgetNumitems(CurrentDir) == 1) { /** Last item in the directory Pop up a directory **/ tempgd = CurrentDir; /** Don't destroy root level directory, or bookmarks **/ if (popgopher(&CurrentDir)==0 && tempgd != CurrentDir) { if (tempgd != BookmarkDir) GDdestroy(tempgd); } else { CursesErrorMsg("Sorry, can't delete top level directory."); scline(-1, 1, CurrentDir); } ChangedDefs = TRUE; } tempgd = GDnew(GDgetNumitems(CurrentDir)+1); for (i=0; i<GDgetNumitems(CurrentDir); i++) { if (i != (GDgetCurrentItem(CurrentDir) - 1)) GDaddGS(tempgd, GDgetEntry(CurrentDir, i)); } GDsetTitle(tempgd, GDgetTitle(CurrentDir)); GDsetCurrentItem(tempgd, GDgetCurrentItem(CurrentDir)); if (GDgetCurrentItem(CurrentDir) == GDgetNumitems(CurrentDir)) GDsetCurrentItem(tempgd, (GDgetCurrentItem(CurrentDir)-1)); GDdestroy(CurrentDir); if (CurrentDir == BookmarkDir) BookmarkDir = tempgd; CurrentDir = tempgd; ChangedDefs = TRUE; break; } case 'M': case 'm': { GopherDirObj *tempGdir = NULL; while (popgopher(&CurrentDir) != -1) { if (tempGdir != NULL) GDdestroy(tempGdir); tempGdir = CurrentDir; } } break; #ifdef VMS /* * I can't get ^C and ^Y working with PASTHRU on VMS, * so we'll just fake it here. */ case 3: case 25: #endif case 'q': /*** Quit the program ***/ if (TRUE == ReallyQuit()) { bDone = TRUE; CURexit(CursesScreen); break; } break; case 'Q': /*** Quit the program, don't ask ***/ bDone = TRUE; CURexit(CursesScreen); break; case 'O': /*** Change various program things ***/ SetOptions(); break; case '=': describe_gopher("Gopher Item Information", GDgetEntry(CurrentDir, GDgetCurrentItem(CurrentDir)-1)); break; case '^': { if (iLevel == 0) describe_gopher("Gopher Directory Information", RootGophers[0]); else describe_gopher("Gopher Directory Information", GDgetEntry(OldDirs[iLevel-1], GDgetCurrentItem(OldDirs[iLevel-1])-1)); break; } case '?': { /*** Display help file ***/ GopherObj *tmpgs; tmpgs = GSnew(); GSsetTitle(tmpgs, "Gopher Help File"); CURexit(CursesScreen); display_file(GOPHERHELP, tmpgs); CURenter(CursesScreen); GSdestroy(tmpgs); break; } default : CURBeep(CursesScreen); Garbled = FALSE; break; } } if (ChangedDefs) write_rc(); GDdestroy(CurrentDir); exit(0); return(0); } int Load_Index(ZeGopher) GopherStruct *ZeGopher; { Draw_Status("Searching Text..."); refresh(); return(Load_Index_or_Dir(ZeGopher, Searchstring)); } int Load_Dir(ZeGopher) GopherStruct *ZeGopher; { Searchstring= NULL; return(Load_Index_or_Dir(ZeGopher, NULL)); } int twirl() { static int twirlnum = 0; static char *twirls = "-/|\\"; addch('\b'); addch(*(twirls + (twirlnum++ % 4 ))); refresh(); return(0); } int Load_Index_or_Dir(ZeGopher, Searchmungestr) GopherStruct *ZeGopher; char *Searchmungestr; { int failed = 0; int sockfd; int i; char sTmp[10]; static char DirTitle[512]; GopherDirObj *NewDir = NULL; NewDir = GDnew(32); sTmp[0]= '\0'; Draw_Status("Connecting..."); refresh(); if ((sockfd = GSconnect(ZeGopher)) <0) { check_sock(sockfd, GSgetHost(ZeGopher), GSgetPort(ZeGopher)); failed = 1; } else { if (GSgetType(ZeGopher) == A_DIRECTORY) { Draw_Status("Retrieving Directory..."); refresh(); writestring(sockfd, GSgetPath(ZeGopher)); writestring(sockfd, "\r\n"); } else if (GSgetType(ZeGopher) == A_INDEX) { Draw_Status("Searching..."); refresh(); writestring(sockfd, GSgetPath(ZeGopher)); writestring(sockfd, "\t"); writestring(sockfd, Searchmungestr); writestring(sockfd, "\r\n"); } i = GDfromNet(NewDir, sockfd, twirl); if (i <= 0) { CursesErrorMsg("Nothing available."); failed = 1; return(failed); } if (GSgetType(ZeGopher) == A_INDEX) { sprintf(DirTitle, "%s: %s", GSgetTitle(ZeGopher), Searchmungestr); GDsetTitle(NewDir, DirTitle); } else GDsetTitle(NewDir, GSgetTitle(ZeGopher)); /** Don't push an empty gopher directory... **/ if (CurrentDir != NULL) pushgopher(CurrentDir); CurrentDir = NewDir; } i = closenet(sockfd); return(failed); } int process_request(ZeGopher) GopherStruct *ZeGopher; { int failed=0; switch(GSgetType(ZeGopher)) { case -1: break; case A_FILE: Draw_Status("Receiving Information..."); refresh(); showfile(ZeGopher); break; case A_GIF: case A_IMAGE: case A_MIME: if (!SecureMode) { Draw_Status("Receiving Information..."); refresh(); showfile(ZeGopher); } else CursesErrorMsg("Sorry Cannot display this file anonymously"); break; case A_MACHEX: case A_PCBIN: case A_UNIXBIN: if (!SecureMode) Save_file(ZeGopher, NULL, NULL); else CursesErrorMsg("Sorry, cannot transfer files in securemode"); break; case A_DIRECTORY: Draw_Status("Receiving Directory..."); refresh(); failed = Load_Dir(ZeGopher); break; case A_TELNET: case A_TN3270: do_tel_3270(ZeGopher); break; case A_INDEX: refresh(); Searchstring = do_index(ZeGopher); Draw_Status("Searching Text..."); if (Searchstring != NULL) failed=Load_Index(ZeGopher); else failed = 1; break; case A_CSO: do_cso(ZeGopher); break; case A_SOUND: Draw_Status("Receiving Sound..."); refresh(); do_sound(ZeGopher); break; case A_HTML: Draw_Status("Receiving HTML page..."); refresh(); do_html(ZeGopher); break; } return(failed); } void describe_gopher(banner, ZeGopher) char *banner; GopherStruct *ZeGopher; { char *tmpfilename; FILE *tmpfile; CURexit(CursesScreen); /* do this *before* possible exit() below */ Gopenfile = tmpfilename = tempnam("/tmp/moo", "gopher"); if ((tmpfile = fopen(tmpfilename, "w")) == NULL) fprintf(stderr, "Couldn't make a tmp file!\n"), exit(-1); fprintf(tmpfile,"Name=%s\nType=%c\nPort=%d\nPath=%s\nHost=%s\n\n", GSgetTitle(ZeGopher), GSgetType(ZeGopher), GSgetPort(ZeGopher), GSgetPath(ZeGopher), GSgetHost(ZeGopher)); fclose(tmpfile); display_file(tmpfilename, ZeGopher); if (unlink(tmpfilename) != 0) fprintf(stderr, "Couldn't unlink!!!\n"), exit(-1); Gopenfile = NULL; free(tmpfilename); CURenter(CursesScreen); /* do this after unlink fails */ return; }